﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Net;

namespace Chat
{
    public class PolicyServer
    {
        protected Socket _policyListenSocket;
        protected int _policyPort = 943;
        protected byte[] _policyFileData = null;

        protected Thread _policyConnectionThread = null;

        protected bool _serverRunning = false;

        public event EventHandler<ServerEventArgs> ServerEvent = delegate { };

        public PolicyServer()
        {
            using (System.IO.Stream stream = System.Reflection.Assembly.GetAssembly(this.GetType()).GetManifestResourceStream("Chat.AccessPolicy.xml"))
            {
                byte[] policyBytes = new byte[stream.Length];
                stream.Read(policyBytes, 0, policyBytes.Length);

                string xml = UTF8Encoding.UTF8.GetString(policyBytes);
                _policyFileData = UTF8Encoding.UTF8.GetBytes(xml);
            }
        }

        public PolicyServer(byte[] policyFileData)
        {
            _policyFileData = policyFileData;
        }

        public void Start()
        {
            _serverRunning = true;

            IPEndPoint policyServerEndPoint = new IPEndPoint(IPAddress.Any, _policyPort);

            _policyListenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _policyListenSocket.Bind(policyServerEndPoint);
            _policyListenSocket.Listen(256);

            _policyConnectionThread = new Thread(policyConnectionHandler);
            _policyConnectionThread.Start();

            ServerEvent(this, new ServerEventArgs(DateTime.Now, "Policy Server listening on port " + _policyPort.ToString()));
        }

        public void Stop()
        {
            _serverRunning = false;
            _policyListenSocket.Close();
        }

        private void policyConnectionHandler()
        {
            while (_serverRunning)
            {
                try
                {
                    // This hangs forever, waiting on new connections.  Killed by the stop method.
                    Socket newSocket = _policyListenSocket.Accept();
                    LogServerEvent("New Policy Connection Received");

                    ThreadPool.QueueUserWorkItem(processPolicyConnection, newSocket);
                }
                catch (SocketException)
                {
                    // should get here from a close ( called by stop )
                }
            }
        }

        private void processPolicyConnection(object state)
        {
            Socket socket = state as Socket;
            byte[] requestBuffer = new byte[1024];

            int byteReceived = socket.Receive(requestBuffer);

            string requestString = UTF8Encoding.UTF8.GetString(requestBuffer, 0, byteReceived);

            LogServerEvent("Received Request: " + requestString);

            if (requestString == "<policy-file-request/>")
            {
                SocketAsyncEventArgs args = new SocketAsyncEventArgs();
                args.SetBuffer(_policyFileData, 0, _policyFileData.Length);
                args.Completed += new EventHandler<SocketAsyncEventArgs>(sendPolicyFile_Complete);
                args.UserToken = socket;

                if (!socket.SendAsync(args))
                    sendPolicyFile_Complete(this, args);

                LogServerEvent("Sent Access Policy");
            }
        }

        private void sendPolicyFile_Complete(object sender, SocketAsyncEventArgs e)
        {
            Socket socket = e.UserToken as Socket;
            socket.Close();
        }

        protected void LogServerEvent(string eventText)
        {
            ServerEvent(this, new ServerEventArgs(DateTime.Now, eventText));
        }
    }
}
